#ifndef __SOCKET_H__
#define __SOCKET_H__
#include <string>
#include <vector>
#include <memory>
#include <cstdint>
#include "time_val.h"

// using namespace cx::time;

// 前置声明
struct sockaddr_in;

/**
 * @brief 获取系统socket错误码
 * @return 错误码（Windows下为WSAGetLastError，Linux下为errno）
 */
int SOCKETERR();

/**
 * @brief 关闭系统socket连接
 * @param fd 待关闭的socket文件描述符
 */
void SOCKETCLOSE(int fd);

namespace cox {
	/**
	 * @brief 网络辅助工具类，提供字节序转换、主机名获取等功能
	 */
	struct NetHelper {
		/**
		 * @brief 网络字节序(short)转换为主机字节序
		 * @param v 网络字节序的16位整数
		 * @return 主机字节序的16位整数
		 */
		static uint16_t Ntohs(uint16_t v);

		/**
		 * @brief 主机字节序(short)转换为网络字节序
		 * @param v 主机字节序的16位整数
		 * @return 网络字节序的16位整数
		 */
		static uint16_t Htons(uint16_t v);

		/**
		 * @brief 网络字节序(long)转换为主机字节序
		 * @param v 网络字节序的32位整数
		 * @return 主机字节序的32位整数
		 */
		static uint32_t Ntohl(uint32_t v);

		/**
		 * @brief 主机字节序(long)转换为网络字节序
		 * @param v 主机字节序的32位整数
		 * @return 网络字节序的32位整数
		 */
		static uint32_t Htonl(uint32_t v);

		/**
		 * @brief 获取当前主机名称
		 * @return 主机名字符串，获取失败返回空字符串
		 */
		static std::string GetHostName();
	};

	/**
	 * @brief 网络地址封装，包含IP/域名和端口
	 * @note 目前仅支持IPv4
	 */
	struct NetAddr {
		std::string host; //!< IP地址（IPv4）或域名
		unsigned    port; //!< 端口号（0-65535）

		/**
		 * @brief 构造函数
		 * @param host IP地址或域名（默认空）
		 * @param port 端口号（默认0）
		 */
		NetAddr(const std::string &host = "", const unsigned &port = 0);

		/**
		 * @brief 从字符串解析IP和端口（格式如"127.0.0.1:80"）
		 * @param ipWithPort 待解析的字符串
		 * @return 解析成功返回true，失败返回false（如端口非数字）
		 * @note 无端口时默认使用80
		 */
		bool parseIPWithPort(const std::string &ipWithPort);

		/**
		 * @brief 转换为系统标准sockaddr_in结构
		 * @param addr 输出参数，存储转换后的sockaddr_in
		 * @return 转换成功返回true，失败返回false（如域名解析失败）
		 */
		bool toSockAddr(struct sockaddr_in *addr) const;
	};

	/**
	 * @brief NetAddr的小于运算符，用于排序或作为map键
	 * @param left 左操作数
	 * @param right 右操作数
	 * @return 按host升序，host相同则按port升序
	 */
	bool operator<(const NetAddr &left, const NetAddr &right);

	/**
	 * @brief 标准socket通信封装类，支持TCP和UDP
	 */
	class Socket {
	public:
		enum Type { UDP, TCP };                    //!< 协议类型
		enum OpType { OP_READ, OP_WRITE, OP_ERR }; //!< select操作类型

		/**
		 * @brief 构造函数（创建新socket）
		 * @param type 协议类型（TCP/UDP）
		 * @note 如果socket创建失败，fd将为-1，后续操作应检查有效性
		 */
		Socket(const Type &type);
		Socket(const Socket &)            = delete;
		Socket &operator=(const Socket &) = delete;

		/**
		 * @brief 析构函数，自动关闭socket
		 */
		virtual ~Socket();

		/**
		 * @brief 判断Socket对象是否有效（fd是否有效）
		 * @return 有效返回true，否则返回false
		 */
		bool isValid() const;

		/**
		 * @brief 加入组播组（仅UDP支持）
		 * @param groupIP 组播地址（如"224.0.0.1"）
		 * @param localIP 本地绑定的IP地址
		 * @return 成功返回true，失败返回false
		 */
		virtual bool addMemberShip(const std::string &groupIP, const std::string &localIP);

		/**
		 * @brief 退出组播组（仅UDP支持）
		 * @param groupIP 组播地址
		 * @param localIP 本地绑定的IP地址
		 * @return 成功返回true，失败返回false
		 */
		virtual bool dropMemberShip(const std::string &groupIP, const std::string &localIP);

		/**
		 * @brief 绑定地址和端口
		 * @param addr 要绑定的网络地址
		 * @return 成功返回true，失败返回false
		 */
		virtual bool bind(const NetAddr &addr);

		/**
		 * @brief 监听连接（仅TCP服务器端）
		 * @param num 最大等待连接队列长度
		 * @return 成功返回true，失败返回false
		 */
		virtual bool listen(const int &num);

		/**
		 * @brief 建立连接（仅TCP客户端）
		 * @param addr 目标服务器地址
		 * @param tv 超时时间（默认阻塞直到连接完成）
		 * @return 成功返回true，失败返回false（包括超时）
		 * @note 当tv>0时，内部会临时切换为非阻塞模式进行连接
		 */
		virtual bool connect(const NetAddr &addr, const TimeVal &tv = TimeVal());

		/**
		 * @brief 重新连接（仅TCP）
		 * @details 此方法会先关闭旧连接，然后创建一个新socket再进行连接。
		 * @param addr 目标服务器地址
		 * @param tv 超时时间
		 * @return 成功返回true，失败返回false
		 */
		virtual bool reconnect(const NetAddr &addr, const TimeVal &tv = TimeVal());

		/**
		 * @brief 判断连接是否已断开（仅TCP）
		 * @return 断开返回true，正常返回false
		 */
		virtual bool isDisconnected() const;

		/**
		 * @brief 接受客户端连接（仅TCP服务器端）
		 * @return 新连接的Socket智能指针，失败返回nullptr
		 * @note 使用智能指针自动管理资源，避免内存泄漏
		 */
		virtual std::unique_ptr<Socket> accept();

		/**
		 * @brief 检查是否可发送数据（非阻塞等待）
		 * @param tv 超时时间（默认立即返回）
		 * @return 可发送返回true，超时或错误返回false
		 */
		virtual bool canSend(const TimeVal &tv = TimeVal(0, 0));

		/**
		 * @brief 发送数据（TCP使用）
		 * @param buf 数据缓冲区
		 * @param len 数据长度
		 * @param flags 发送标志（如MSG_NOSIGNAL）
		 * @return 成功发送的字节数，失败返回-1
		 */
		virtual long long send(const char *buf, const size_t len, const int flags = 0);

		/**
		 * @brief 向指定地址发送数据（UDP使用）
		 * @param buf 数据缓冲区
		 * @param len 数据长度
		 * @param addr 目标地址
		 * @param flags 发送标志
		 * @return 成功发送的字节数，失败返回-1
		 */
		virtual long long sendto(const char *buf, const size_t len, const NetAddr &addr, const int flags = 0);

		/**
		 * @brief 检查是否可接收数据（非阻塞等待）
		 * @param tv 超时时间（默认立即返回）
		 * @return 可接收返回true，超时或错误返回false
		 */
		virtual bool canRecv(const TimeVal &tv = TimeVal(0, 0));

		/**
		 * @brief 接收数据（TCP使用）
		 * @param buf 接收缓冲区
		 * @param len 缓冲区最大长度
		 * @param flags 接收标志（如MSG_PEEK）
		 * @return 成功接收的字节数，0表示连接关闭，-1表示错误
		 */
		virtual long long recv(char *buf, const size_t len, const int flags = 0);

		/**
		 * @brief 接收数据并获取发送方地址（UDP使用）
		 * @param buf 接收缓冲区
		 * @param len 缓冲区最大长度
		 * @param addr 输出参数，存储发送方地址
		 * @param flags 接收标志
		 * @return 成功接收的字节数，-1表示错误
		 */
		virtual long long recvfrom(char *buf, const size_t len, NetAddr &addr, const int flags = 0);

		/**
		 * @brief 获取内部socket文件描述符
		 * @return 文件描述符（-1表示无效）
		 */
		virtual int getFd() const;

		/**
		 * @brief 获取最近一次错误码
		 * @return 错误码（通过SOCKETERR()获取）
		 */
		virtual int getError() const;

		/**
		 * @brief 获取当前绑定的地址
		 * @return 网络地址对象
		 */
		virtual NetAddr getAddr() const;

		/**
		 * @brief 设置地址和端口复用
		 * @param on true启用复用，false禁用
		 * @return 成功返回true，失败返回false
		 */
		virtual bool setReuseAddr(bool on = true);

		/**
		 * @brief 设置socket为非阻塞模式
		 * @param on true启用非阻塞，false禁用（阻塞模式）
		 * @return 成功返回true，失败返回false
		 */
		virtual bool setNotBlock(bool on = true);

		/**
		 * @brief 设置关闭时的 linger 行为
		 * @param on true启用linger，false禁用
		 * @param linger 等待时间（秒），仅on为true时有效
		 * @return 成功返回true，失败返回false
		 * @note 若on=true且linger=0，将强制关闭连接（丢弃未发送数据）
		 */
		virtual bool setLinger(bool on = true, unsigned linger = 30);

		/**
		 * @brief 添加路由（未实现）
		 * @details 此功能高度依赖操作系统，且通常需要管理员权限，
		 *          故未在本通用库中实现。建议使用系统命令或特定API。
		 * @param dest 目标网络
		 * @param gate 网关地址
		 * @param mask 子网掩码（默认0xffffff00）
		 * @return 始终返回false
		 */
		static bool routeAdd(const char *dest, const char *gate, long mask = 0xffffff00);

		/**
		 * @brief 反转数据字节序（用于大小端转换）
		 * @param data 数据缓冲区
		 * @param len 数据长度
		 */
		static void reverseSeq(char *data, const size_t len);

		/**
		 * @brief 将当前socket的fd加入fd_set
		 * @param type 操作类型（读/写/错误）
		 * @param fdSet fd_set指针
		 * @return 当前socket的fd
		 */
		virtual int setFdSet(OpType type, void *fdSet);

		/**
		 * @brief 检查当前socket的fd是否在fd_set中
		 * @param type 操作类型（读/写/错误）
		 * @param fdSet fd_set指针
		 * @return 在集合中返回true，否则返回false
		 */
		virtual bool matchFdSet(OpType type, void *fdSet);

		/**
		 * @brief 封装系统select函数（操作fd列表）
		 * @param readMap 输入输出，待检查可读的fd列表，输出就绪的fd
		 * @param writeMap 输入输出，待检查可写的fd列表，输出就绪的fd
		 * @param errMap 输入输出，待检查错误的fd列表，输出就绪的fd
		 * @param tv 超时时间
		 * @return 就绪的fd总数，-1表示错误，0表示超时
		 */
		static int select(std::vector<int> *readMap = nullptr, std::vector<int> *writeMap = nullptr, std::vector<int> *errMap = nullptr, const TimeVal &tv = TimeVal());

		/**
		 * @brief 封装系统select函数（操作Socket指针列表）
		 * @param readVec 输入输出，待检查可读的Socket列表，输出就绪的Socket
		 * @param writeVec 输入输出，待检查可写的Socket列表，输出就绪的Socket
		 * @param errVec 输入输出，待检查错误的Socket列表，输出就绪的Socket
		 * @param tv 超时时间
		 * @return 就绪的Socket总数，-1表示错误，0表示超时
		 */
		static int select(std::vector<Socket *> *readVec, std::vector<Socket *> *writeVec = nullptr, std::vector<Socket *> *errVec = nullptr, const TimeVal &tv = TimeVal());

		/**
		 * @brief 获取调试信息
		 * @return 包含类型和fd的字符串
		 */
		virtual std::string getInfo() const;

	protected:
		const Type type; //!< 协议类型（TCP/UDP）
		int        fd;   //!< socket文件描述符（-1表示无效）
		NetAddr    addr; //!< 绑定的地址

	private:
		/**
		 * @brief 初始化socket（内部使用）
		 * @return 成功返回true，失败返回false
		 */
		bool initSocket();

		/**
		 * @brief 构造函数（用于accept创建新连接）
		 * @param fd 已连接的socket fd
		 * @param addr 客户端地址
		 */
		Socket(const int &fd, const NetAddr &addr);
	};
} // namespace cox
#endif // SOCKETADAPTER_H
